﻿/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://github.com/shenyczz/CSharpKit
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using CSharpKit.Extensions;

namespace CSharpKit.Numerics
{
    /// <summary>
    /// 数值操作，返回精确的期望类型
    /// </summary>
    public static class NumOperators_
    {
        // +(Add), -(Sub), *(Mul), /(Div), %(Mod)
        // +(Add), -(Subtract), *(Multiply), /(Divide), %(Module)

        // Unmanaged => bool、sbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal


        #region Add [+]

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Add<U, V>(in U lhs, in V rhs, out bool vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = lhs.BoolValue() || rhs.BoolValue();
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Add<U, V>(in U lhs, in V rhs, out byte vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (byte)(lhs.ByteValue() + rhs.ByteValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Add<U, V>(in U lhs, in V rhs, out char vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (char)(lhs.CharValue() + rhs.CharValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Add<U, V>(in U lhs, in V rhs, out short vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (short)(lhs.Int16Value() + rhs.Int16Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Add<U, V>(in U lhs, in V rhs, out int vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (int)(lhs.Int32Value() + rhs.Int32Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Add<U, V>(in U lhs, in V rhs, out long vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (long)(lhs.Int64Value() + rhs.Int64Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Add<U, V>(in U lhs, in V rhs, out float vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (float)(lhs.SingleValue() + rhs.SingleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Add<U, V>(in U lhs, in V rhs, out double vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (double)(lhs.DoubleValue() + rhs.DoubleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Add<U, V>(in U lhs, in V rhs, out decimal vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (decimal)(lhs.DecimalValue() + rhs.DecimalValue());
        }

        #endregion


        #region Sub [-]

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Sub<U, V>(in U lhs, in V rhs, out bool vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (lhs.Int32Value() - rhs.Int32Value()).BoolValue();
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Sub<U, V>(in U lhs, in V rhs, out byte vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (byte)(lhs.ByteValue() - rhs.ByteValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Sub<U, V>(in U lhs, in V rhs, out char vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (char)(lhs.CharValue() - rhs.CharValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Sub<U, V>(in U lhs, in V rhs, out short vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (short)(lhs.Int16Value() - rhs.Int16Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Sub<U, V>(in U lhs, in V rhs, out int vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (int)(lhs.Int32Value() - rhs.Int32Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Sub<U, V>(in U lhs, in V rhs, out long vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (long)(lhs.Int64Value() - rhs.Int64Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Sub<U, V>(in U lhs, in V rhs, out float vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (float)(lhs.SingleValue() - rhs.SingleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Sub<U, V>(in U lhs, in V rhs, out double vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (double)(lhs.DoubleValue() - rhs.DoubleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Sub<U, V>(in U lhs, in V rhs, out decimal vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (decimal)(lhs.DecimalValue() - rhs.DecimalValue());
        }

        #endregion


        #region Mul [*]

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mul<U, V>(in U lhs, in V rhs, out bool vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (lhs.Int32Value() * rhs.Int32Value()).BoolValue();
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mul<U, V>(in U lhs, in V rhs, out byte vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (byte)(lhs.ByteValue() * rhs.ByteValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mul<U, V>(in U lhs, in V rhs, out char vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (char)(lhs.CharValue() * rhs.CharValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mul<U, V>(in U lhs, in V rhs, out short vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (short)(lhs.Int16Value() * rhs.Int16Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mul<U, V>(in U lhs, in V rhs, out int vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (int)(lhs.Int32Value() * rhs.Int32Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mul<U, V>(in U lhs, in V rhs, out long vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (long)(lhs.Int64Value() * rhs.Int64Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mul<U, V>(in U lhs, in V rhs, out float vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (float)(lhs.SingleValue() * rhs.SingleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mul<U, V>(in U lhs, in V rhs, out double vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (double)(lhs.DoubleValue() * rhs.DoubleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mul<U, V>(in U lhs, in V rhs, out decimal vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (decimal)(lhs.DecimalValue() * rhs.DecimalValue());
        }

        #endregion


        #region Div [/]

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Div<U, V>(in U lhs, in V rhs, out bool vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (lhs.Int32Value() / rhs.Int32Value()).BoolValue();
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Div<U, V>(in U lhs, in V rhs, out byte vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (byte)(lhs.ByteValue() / rhs.ByteValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Div<U, V>(in U lhs, in V rhs, out char vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (char)(lhs.CharValue() / rhs.CharValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Div<U, V>(in U lhs, in V rhs, out short vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (short)(lhs.Int16Value() / rhs.Int16Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Div<U, V>(in U lhs, in V rhs, out int vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (int)(lhs.Int32Value() / rhs.Int32Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Div<U, V>(in U lhs, in V rhs, out long vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (long)(lhs.Int64Value() / rhs.Int64Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Div<U, V>(in U lhs, in V rhs, out float vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (float)(lhs.SingleValue() / rhs.SingleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Div<U, V>(in U lhs, in V rhs, out double vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (double)(lhs.DoubleValue() / rhs.DoubleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Div<U, V>(in U lhs, in V rhs, out decimal vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (decimal)(lhs.DecimalValue() / rhs.DecimalValue());
        }

        #endregion


        #region Mod [%]

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mod<U, V>(in U lhs, in V rhs, out bool vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (lhs.Int32Value() % rhs.Int32Value()).BoolValue();
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mod<U, V>(in U lhs, in V rhs, out byte vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (byte)(lhs.ByteValue() % rhs.ByteValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mod<U, V>(in U lhs, in V rhs, out char vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (char)(lhs.CharValue() % rhs.CharValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mod<U, V>(in U lhs, in V rhs, out short vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (short)(lhs.Int16Value() % rhs.Int16Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mod<U, V>(in U lhs, in V rhs, out int vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (int)(lhs.Int32Value() % rhs.Int32Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mod<U, V>(in U lhs, in V rhs, out long vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (long)(lhs.Int64Value() % rhs.Int64Value());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mod<U, V>(in U lhs, in V rhs, out float vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (float)(lhs.SingleValue() % rhs.SingleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mod<U, V>(in U lhs, in V rhs, out double vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (double)(lhs.DoubleValue() % rhs.DoubleValue());
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        public static void Mod<U, V>(in U lhs, in V rhs, out decimal vout)
            where U : struct, IEquatable<U>
            where V : struct, IEquatable<V>
        {
            vout = (decimal)(lhs.DecimalValue() % rhs.DecimalValue());
        }

        #endregion


        #region 四则运算（+、-、*、/、%） - Four Fundamental Rules

        // 被加数 addend 
        // 加数   addisor

        // 被除数 divdend
        // 除数   divisor

        // 被乘数 facdend   multiplicaend
        // 乘数   facisor   multiplicator

        // 被减数 minudend subtracted
        // 减数   minuisor subtractor

        public static T Negate<T>(in T x)
            where T : struct, IFormattable, IEquatable<T>
        {
            dynamic result = x;

            var tc = typeof(T).GetTypeCode();

            switch (tc)
            {

                case NumTypeCode.Boolean:
                    result = !x.BoolValue();
                    break;
                case NumTypeCode.Char:
                    result = -x.CharValue();
                    break;
                case NumTypeCode.SByte:
                    result = -x.SByteValue();
                    break;
                case NumTypeCode.Byte:
                    result = -x.ByteValue();
                    break;
                case NumTypeCode.Int16:
                    result = -x.Int16Value();
                    break;
                case NumTypeCode.UInt16:
                    result = -x.UInt16Value();
                    break;
                case NumTypeCode.Int32:
                    result = -x.Int32Value();
                    break;
                case NumTypeCode.UInt32:
                    result = -x.Int32Value();
                    break;
                case NumTypeCode.Int64:
                    result = -x.Int64Value();
                    break;
                case NumTypeCode.UInt64:
                    result = -x.Int64Value();
                    break;
                case NumTypeCode.Single:
                    result = -x.SingleValue();
                    break;
                case NumTypeCode.Double:
                    result = -x.DoubleValue();
                    break;
                case NumTypeCode.Decimal:
                    result = -x.DecimalValue();
                    break;

                case NumTypeCode.Empty:
                    break;
                case NumTypeCode.Object:
                    break;
                case NumTypeCode.DBNull:
                    break;
                case NumTypeCode.String:
                    break;
                case NumTypeCode.Complex:
                    break;

                default:
                    break;
            }
            //vout = rhs.BoolValue() || rhs.BoolValue();

            return (dynamic)result;
        }

        public static T Add<T>(T x, T y) => (dynamic)x + y;
        public static T Subtract<T>(T x, T y) => (dynamic)x - y;
        public static T Multiply<T>(T x, T y) => (dynamic)x * y;
        public static T Divide<T>(T x, T y) => (dynamic)x / y;
        public static T Modulo<T>(T x, T y) => (dynamic)x % y;

        #endregion


        #region 数值比较（>、>=、<、<=） - Numerical Comparison

        public static bool GreatThan<T>(T x, T y) => (dynamic)x > y;
        public static bool GreatEqualThan<T>(T x, T y) => (dynamic)x >= y;
        public static bool LessThan<T>(T x, T y) => (dynamic)x < y;
        public static bool LessEqualThan<T>(T x, T y) => (dynamic)x <= y;

        #endregion




        // 数值运算 - Numerical Evaluation

        public static T Abs<T>(T x) => System.Math.Abs((dynamic)x);

        // _stopCriterias.Select(sc => sc.Clone())
        public static T Sum<T>(IEnumerable<T> xs) => (dynamic)xs.Sum(x => (dynamic)x);



        // Trigonometric Function - 三角函数

        public static T Acos<T>(T x) => System.Math.Acos((dynamic)x);
        //public static T Acosh<T>(T x) => Math.Acosh((dynamic)x);    // netcore

        public static T Cos<T>(T x) => System.Math.Cos((dynamic)x);
        public static T Sin<T>(T x) => System.Math.Sin((dynamic)x);

        public static T Log<T>(T x) => System.Math.Log((dynamic)x);
        public static T Log10<T>(T x) => System.Math.Log10((dynamic)x);

        public static T Exp<T>(T x) => System.Math.Exp((dynamic)x);



        public static T Tan<T>(T x) => System.Math.Tan((dynamic)x);
        public static T Tanh<T>(T x) => System.Math.Tanh((dynamic)x);

        public static T Pow<T>(T x, T y) => System.Math.Pow((dynamic)x, (dynamic)y);
        public static T Sqrt<T>(T x) => System.Math.Sqrt((dynamic)x);



        // 矩阵相乘
        public static T[,] MatrixMultiply<T>(T[,] lhs, T[,] rhs)
        {
            var result = default(T[,]);

            int lrow = lhs.GetLength(0);
            int lcol = lhs.GetLength(1);

            int rrow = rhs.GetLength(0);
            int rcol = rhs.GetLength(1);

            if (lcol != rrow)
            {
                // 不符合矩阵乘法
                throw new Exception();
            }

            // 左行，右列
            result = new T[lrow, rcol];

            for (var i = 0; i < lrow; i++)
            {
                for (var j = 0; j < rcol; j++)
                {
                    var s = (dynamic)0;
                    for (var k = 0; k < lcol; k++)
                    {
                        s += Multiply(lhs[i, k], rhs[k, j]);
                    }
                    result[i, j] = s;
                }
            }

            return result;
        }





        //}}@@@
    }



}

